
namespace Oddo.Concept.Crm.Common.Communication.Security
{
    using System;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Security.Principal;
    using System.Text;
    using System.Threading;
    using System.Web.Http;
    using System.Web.Http.Controllers;

    public class BasicHttpAutorizeAttribute : System.Web.Http.AuthorizeAttribute
    {
        private const string BASIC_AUTH_RESPONSE_HEADER_VALUE = "Basic";

        public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            if (actionContext == null)throw new ArgumentNullException("actionContext");

            if (SkipAuthorization(actionContext) || IsAuthorized(actionContext))
                return;

            this.HandleUnauthorizedRequest(actionContext);
        }
        
        protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
        {
            if (actionContext == null)throw new ArgumentNullException("actionContext");

            var response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized,"Access not authorized");
            actionContext.Response = response;
        }

        protected override bool IsAuthorized(HttpActionContext actionContext)
        {
            var request = actionContext.Request;
            AuthenticationHeaderValue authValue = request.Headers.Authorization;
            if (authValue == null || String.IsNullOrWhiteSpace(authValue.Parameter)
                || String.IsNullOrWhiteSpace(authValue.Scheme)
                || !string.Equals(authValue.Scheme, BASIC_AUTH_RESPONSE_HEADER_VALUE,StringComparison.InvariantCultureIgnoreCase))
            {
                return false;
            }

            string[] parsedHeader = ParseAuthorizationHeader(authValue.Parameter);
            if (parsedHeader == null)
            {
                return false;
            }

            IPrincipal principal = null;
            if (TryCreatePrincipal(parsedHeader[0], parsedHeader[1], out principal))
            {
                Thread.CurrentPrincipal = principal;
                var result =  CheckRoles(principal) && CheckUsers(principal);
                return result;
            }

            return false;
        }
        private static bool SkipAuthorization(HttpActionContext actionContext)
        {
            if(actionContext ==null)throw new ArgumentNullException("actionContext");
            return actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any() || 
                actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any();
        }

        private static string[] ParseAuthorizationHeader(string authHeader)
        {
            string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(authHeader)).Split(new[] { ':' });
            if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[1]))
                return null;
            return credentials;
        }
        
        private static bool TryCreatePrincipal(string user, string password, out IPrincipal principal)
        {
            principal = null;
            var msp = new CustomMemberShipProvider();
            if (msp.ValidateUser(user, password))
                principal = new GenericPrincipal(new GenericIdentity(user), null);
            return principal != null;
        }

        private bool CheckUsers(IPrincipal principal)
        {
            string[] users = UsersSplit;
            if (users.Length == 0) return true;
            return users.Any(u => principal.Identity.Name == u);
        }
        private bool CheckRoles(IPrincipal principal)
        {
            string[] roles = RolesSplit;
            if (roles.Length == 0) return true;
            return roles.Any(principal.IsInRole);
        }
        protected string[] RolesSplit
        {
            get { return SplitStrings(this.Roles); }
        }
        protected string[] UsersSplit
        {
            get { return SplitStrings(this.Users); }
        }
        protected static string[] SplitStrings(string input)
        {
            if (string.IsNullOrWhiteSpace(input)) return new string[0];
            var result = input.Split(',').Where(s => !String.IsNullOrWhiteSpace(s.Trim()));
            return result.Select(s => s.Trim()).ToArray();
        }
    }
}